#pragma once

#include "error.h"

namespace sfc::io {

template <class I>
struct Write {
  auto write_str(Str s) -> usize {
    auto& self = static_cast<I&>(*this);
    return self.write(s.as_bytes());
  }
};

template <class W>
struct BufWriter : Write<BufWriter<W>> {
  W _inn;
  Vec<u8> _buf;

  explicit BufWriter(W&& inn, Vec<u8>&& buf = Vec<u8>::with_capacity(1024))
      : _inn{mem::move(inn)}, _buf{mem::move(buf)} {}

  ~BufWriter() {
    this->flush();
  }

  auto write(Slice<const u8> buf) -> usize {
    if (buf.len() + _buf.len() > _buf.cap()) {
      this->flush();
    }
    if (buf.len() >= _buf.cap()) {
      return _inn.write(buf);
    }
    _buf.extend(buf);
    return buf.len();
  }

  void flush() {
    _inn.write(_buf);
    _buf.clear();
  }
};

template <class W>
struct LineWriter : Write<LineWriter<W>> {
  BufWriter<W> _inn;

  explicit LineWriter(W&& inn, Vec<u8>&& buf = Vec<u8>::with_capacity(1024))
      : _inn{mem::move(inn), mem::move(buf)} {}

  auto write(Slice<const u8> buf) -> usize {
    if (auto pos = buf.find(cmp::Eq{u8'\n'})) {
      const auto [a, b] = buf.split_at(~pos);
      _inn.write(a);
      _inn.flush();
      _inn.write(b);
      return buf.len();
    }
    return _inn.write(buf);
  }

  void flush() {
    return _inn.flush();
  }
};

}  // namespace sfc::io
